package org.df.ulab.bot.util.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * @author Daniel Filgueiras
 * @since 19/06/2011
 */
public final class StorageSystem {

	private static final Logger logger = LoggerFactory.getLogger(StorageSystem.class);
	
	private static final ThreadLocal<StorageSystem> instance;
	
	static {
		instance = new InheritableThreadLocal<StorageSystem>();
	}
	
	private final String rootPath;
	private final String enginePath;
	
	private XStream stream = new XStream(new DomDriver());
	private Map<String,Object> objects = new HashMap<String, Object>();
	
	private StorageSystem(String id) {
		rootPath = new File("").getAbsolutePath() + "/config/";
		File directory = new File(rootPath);
		if ( !(directory.exists() && directory.isDirectory()) )
			directory.mkdir();
		
		enginePath = rootPath + id + "/";
		directory = new File(enginePath);
		if ( !(directory.exists() && directory.isDirectory()) )
			directory.mkdir();
			
	}
	
	public static synchronized void init(final String id) {
		instance.set(new StorageSystem(id));
	}
	
	
	public static StorageSystem getInstance() {
		return instance.get();
	}
	
	public <T extends Serializable> T load(Class<T> clazz) {
		return load(clazz, null, false);
	}
	
	@SuppressWarnings("unchecked")
	public <T extends Serializable> T load(Class<T> clazz, String scope, boolean autoCreate) {
		
		String keyName = getKeyName(clazz, scope);
		
		synchronized (objects) {
			T obj;
			try {
				obj = (T) objects.get(keyName);
				if (obj != null) 
					return obj;
					
				obj = (T) loadFile(keyName);
				if (obj == null) 
					obj = clazz.newInstance(); 
					
				objects.put(keyName, obj);
				
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
			
			return obj;	
		}
	}
	
	private Object loadFile(String name) {
		try {
			return stream.fromXML(new FileInputStream(name));
		} catch(ConversionException e) {
			logger.warn("File is deprecated {}", name, e);
		} catch (FileNotFoundException e) {
			logger.warn("File not found {}", name);
		}
		return null;
	}
	
	public void persist() {
		try {
			synchronized (objects) {
				XStream stream = new XStream(new DomDriver());
				
				for (Entry<String, Object> entry : objects.entrySet()) {
					stream.toXML(entry.getValue(), new PrintWriter(entry.getKey()));
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public Configuration getConfiguration(String name) {
		try {
			if (new File(enginePath + name).exists())
				return new XMLConfiguration(enginePath + name);
			else
				return new XMLConfiguration(rootPath + name);
		} catch (ConfigurationException e) {
			logger.error("Fail creating configuration", e);
		}
		return null;
	}
	
	private String getKeyName(Class<?> clazz, String scope)  {
		if (scope != null)
			return enginePath + clazz.getSimpleName() + "-" + scope + ".xml";
		else
			return enginePath + clazz.getSimpleName() + ".xml";
	}
}
